#pragma once

#include "LoopNode.h"

#include "Action.h"
#include "ActivityNode.h"
#include "ExecutableNode.h"
#include "InputPin.h"
#include "OutputPin.h"

namespace uml {

const bool LoopNode::isTestedFirst_default_ = false;
LoopNode::LoopNode(const QString& umlType)
    : StructuredActivityNode(umlType) {}

void LoopNode::fromJson(const QJsonObject& json, QStringList& errorList) {
    if (json.isEmpty()) {
        return;
    }

    StructuredActivityNode::fromJson(json, errorList);
    JsonSerializable::fromJsonArray(bodyOutputs_, "bodyOutputs", json, errorList);
    JsonSerializable::fromJsonArray(bodyParts_, "bodyParts", json, errorList);
    decider_ = JsonSerializable::create<OutputPin>(json.value("decider"), errorList);

    isTestedFirst_ = json.value("isTestedFirst").toBool(isTestedFirst_default_);
    JsonSerializable::fromJsonArray(loopVariables_, "loopVariables", json, errorList);
    JsonSerializable::fromJsonArray(loopVariableInputs_, "loopVariableInputs", json, errorList);
    JsonSerializable::fromJsonArray(results_, "results", json, errorList);
    JsonSerializable::fromJsonArray(setupParts_, "setupParts", json, errorList);
    JsonSerializable::fromJsonArray(tests_, "tests", json, errorList);
}

void LoopNode::toJson(QJsonObject& json) {
    StructuredActivityNode::toJson(json);
    JsonSerializable::toJsonArray(bodyOutputs_, "bodyOutputs", json);
    JsonSerializable::toJsonArray(bodyParts_, "bodyParts", json);
    if (decider_) {
        QJsonObject decider_json;
        decider_->toJson(decider_json);
        json.insert("decider", decider_json);
    }
    if (isTestedFirst_ != isTestedFirst_default_) {
        json.insert("isTestedFirst", isTestedFirst_);
    }
    JsonSerializable::toJsonArray(loopVariables_, "loopVariables", json);
    JsonSerializable::toJsonArray(loopVariableInputs_, "loopVariableInputs", json);
    JsonSerializable::toJsonArray(results_, "results", json);
    JsonSerializable::toJsonArray(setupParts_, "setupParts", json);
    JsonSerializable::toJsonArray(tests_, "tests", json);
}

/// <summary>
/// allActions
    /// Return only this LoopNode. This prevents Actions within the LoopNode from having
    /// their OutputPins used as bodyOutputs or decider Pins in containing LoopNodes or
    /// ConditionalNodes.
/// </summary>
    /// <code>
    /// result = (self->asSet())
    /// </code>
shared_ptr<vector<Action>> LoopNode::allActions() const
{
    return nullptr;
}

/// <summary>
/// sourceNodes
    /// Return the loopVariable OutputPins in addition to other source nodes for the
    /// LoopNode as a StructuredActivityNode.
/// </summary>
    /// <code>
    /// result = (self.StructuredActivityNode::sourceNodes()->union(loopVariable))
    /// </code>
shared_ptr<vector<ActivityNode>> LoopNode::sourceNodes() const
{
    return nullptr;
}

/// <summary>
/// bodyOutputs getter
/// The OutputPins on Actions within the bodyPart, the values of which are moved to
/// the loopVariable OutputPins after the completion of each execution of the
/// bodyPart, before the next iteration of the loop begins or before the loop exits.
/// </summary>
vector<shared_ptr<OutputPin>>& LoopNode::bodyOutputs() {
    return bodyOutputs_;
}

/// <summary>
/// bodyParts getter
/// The set of ExecutableNodes that perform the repetitive computations of the loop.
/// The bodyPart is executed as long as the test section produces a true value.
/// </summary>
vector<shared_ptr<ExecutableNode>>& LoopNode::bodyParts() {
    return bodyParts_;
}

/// <summary>
/// decider getter
/// An OutputPin on an Action in the test section whose Boolean value determines
/// whether to continue executing the loop bodyPart.
/// </summary>
shared_ptr<OutputPin> LoopNode::decider() const {
    return decider_;
}

/// <summary>
/// decider setter
/// An OutputPin on an Action in the test section whose Boolean value determines
/// whether to continue executing the loop bodyPart.
/// </summary>
void LoopNode::setDecider(shared_ptr<OutputPin> value) {
    decider_ = value;
}

/// <summary>
/// isTestedFirst getter
/// If true, the test is performed before the first execution of the bodyPart. If
/// false, the bodyPart is executed once before the test is performed.
/// </summary>
bool LoopNode::isTestedFirst() const {
    return isTestedFirst_;
}

/// <summary>
/// isTestedFirst setter
/// If true, the test is performed before the first execution of the bodyPart. If
/// false, the bodyPart is executed once before the test is performed.
/// </summary>
void LoopNode::setIsTestedFirst(bool value) {
    isTestedFirst_ = value;
}

/// <summary>
/// loopVariables getter
/// A list of OutputPins that hold the values of the loop variables during an
/// execution of the loop. When the test fails, the values are moved to the result
/// OutputPins of the loop.
/// </summary>
vector<shared_ptr<OutputPin>>& LoopNode::loopVariables() {
    return loopVariables_;
}

/// <summary>
/// loopVariableInputs getter
/// A list of InputPins whose values are moved into the loopVariable Pins before the
/// first iteration of the loop.
/// </summary>
vector<shared_ptr<InputPin>>& LoopNode::loopVariableInputs() {
    return loopVariableInputs_;
}

/// <summary>
/// results getter
/// A list of OutputPins that receive the loopVariable values after the last
/// iteration of the loop and constitute the output of the LoopNode.
/// </summary>
vector<shared_ptr<OutputPin>>& LoopNode::results() {
    return results_;
}

/// <summary>
/// setupParts getter
/// The set of ExecutableNodes executed before the first iteration of the loop, in
/// order to initialize values or perform other setup computations.
/// </summary>
vector<shared_ptr<ExecutableNode>>& LoopNode::setupParts() {
    return setupParts_;
}

/// <summary>
/// tests getter
/// The set of ExecutableNodes executed in order to provide the test result for the
/// loop.
/// </summary>
vector<shared_ptr<ExecutableNode>>& LoopNode::tests() {
    return tests_;
}

} // namespace uml
